home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2008 January / Mac_easy_01_08.iso / Software / Online / Chat / macam.0.9.1.dmg / macam source / utilities / BayerConverter.m < prev    next >
Encoding:
Text File  |  2006-07-25  |  49.7 KB  |  1,298 lines

  1. /*
  2.  macam - webcam app and QuickTime driver component
  3.  Copyright (C) 2002 Matthias Krauss (macam@matthias-krauss.de)
  4.  
  5.  This program is free software; you can redistribute it and/or modify
  6.  it under the terms of the GNU General Public License as published by
  7.  the Free Software Foundation; either version 2 of the License, or
  8.  (at your option) any later version.
  9.  
  10.  This program is distributed in the hope that it will be useful,
  11.  but WITHOUT ANY WARRANTY; without even the implied warranty of>
  12.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  GNU General Public License for more details.
  14.  
  15.  You should have received a copy of the GNU General Public License
  16.  along with this program; if not, write to the Free Software
  17.  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  $Id: BayerConverter.m,v 1.15 2006/07/25 05:17:07 hxr Exp $
  19.  */
  20. #import "BayerConverter.h"
  21.  
  22.  
  23. @interface BayerConverter (Private)
  24. - (void) demosaicFrom:(unsigned char*)src type:(short)type srcRowBytes:(long)srcRowBytes;
  25.                //Type: 1=STV680-style, 2=STV600-style
  26. - (void) postprocessGRBGTo:(unsigned char*)dst dstRowBytes:(long)dstRowBytes dstBPP:(short)dstBPP flip:(BOOL)flip;
  27. - (void) calcColorStatistics;
  28. - (void) updateGainsToColorStats;
  29. - (void) recalcTransferLookup;
  30. - (void) rotateImage180;
  31.  
  32. @end
  33.  
  34. @implementation BayerConverter
  35.  
  36. - (id) init {
  37.     self=[super init];
  38.     brightness=0.0f;
  39.     contrast=1.0f;
  40.     gamma=1.0f;
  41.     saturation=65536;
  42.     sharpness=0.0f;
  43.     rgbBuffer=NULL;
  44.     sourceWidth=0;
  45.     sourceHeight=0;
  46.     destinationWidth=0;
  47.     destinationHeight=0;
  48.     sourceFormat=1;
  49.     updateGains=NO;
  50.     produceColorStats=NO;
  51.     redGain=1.0f;
  52.     greenGain=1.0f;
  53.     blueGain=1.0f;
  54.     [self recalcTransferLookup];
  55.     return self;
  56. }
  57.  
  58. - (void) dealloc {
  59.     if (rgbBuffer) FREE(rgbBuffer,"BayerConverter dealloc rgbBuffer"); rgbBuffer=NULL;
  60.     [super dealloc];
  61. }
  62.  
  63. - (unsigned long) sourceWidth { return sourceWidth; }
  64.  
  65. - (unsigned long) sourceHeight { return sourceHeight; }
  66.  
  67. - (void) setSourceWidth:(long)width height:(long)height {
  68.     BOOL sizeChanged=((sourceWidth*sourceHeight)!=(width*height));
  69.     if ((sizeChanged)&&(rgbBuffer)) {
  70.         FREE (rgbBuffer,"BayerDecoder setSourceWidth:height: rgbBuffer");
  71.         rgbBuffer=NULL;
  72.     }
  73.     if (!rgbBuffer) {
  74.         MALLOC(rgbBuffer,unsigned char*,width*height*3,"BayerDecoder setSourceWidth:height: rgbBuffer");
  75.     }
  76.     sourceWidth=width;
  77.     sourceHeight=height;
  78. }
  79.  
  80. - (short) sourceFormat {
  81.     return sourceFormat;
  82. }
  83.  
  84. - (void) setSourceFormat:(short)fmt {
  85.     if ((fmt<1)||(fmt>MAX_BAYER_TYPE)) return;
  86.     sourceFormat=fmt;
  87. }
  88.  
  89. - (unsigned long) destinationWidth { return destinationWidth; }
  90.  
  91. - (unsigned long) destinationHeight { return destinationHeight; }
  92.  
  93. - (void) setDestinationWidth:(long)width height:(long)height {
  94.     destinationWidth=width;
  95.     destinationHeight=height;
  96. }
  97.  
  98. - (float) brightness { return brightness; }
  99.  
  100. - (void) setBrightness:(float)newBrightness {
  101.     brightness=CLAMP(newBrightness,-1.0f,1.0f);
  102.     [self recalcTransferLookup];
  103. }
  104.  
  105. - (float) contrast { return contrast; }
  106.  
  107. - (void) setContrast:(float)newContrast {
  108.     contrast=CLAMP(newContrast,0.0f,2.0f);
  109.     [self recalcTransferLookup];
  110. }
  111.  
  112. - (float) gamma { return gamma; }
  113. - (void) setGamma:(float)newGamma {
  114.     gamma=CLAMP(newGamma,0.0f,2.0f);
  115.     [self recalcTransferLookup];
  116. }
  117.  
  118. - (float) saturation { return ((float)saturation)/65536.0f; }
  119. - (void) setSaturation:(float)newSaturation {
  120.     saturation=65536.0f*CLAMP(newSaturation,0.0f,2.0f);
  121. }
  122.  
  123. - (float) sharpness { return sharpness; }
  124.  
  125. - (void) setSharpness:(float)newSharpness {
  126.     sharpness=CLAMP(newSharpness,0.0f,1.0f);
  127. }
  128.  
  129. - (void) setGainsDynamic:(BOOL)dynamic {
  130.     updateGains=dynamic;
  131.     averageSumsValid=NO;
  132. }
  133.  
  134. - (void) setGainsRed:(float)r green:(float)g blue:(float)b {
  135.     redGain=r;
  136.     greenGain=g;
  137.     blueGain=b;
  138.     [self recalcTransferLookup];
  139. }
  140.  
  141. - (void) setMakeImageStats:(BOOL)on {
  142.     //lastMeanStats should return a negative value to indicate an invalid average
  143.     if ((!produceColorStats)||(!on)) {
  144.         meanRed=meanGreen=meanBlue=-1.0f;
  145.     }
  146.     produceColorStats=on;
  147.  
  148. }
  149.  
  150. - (float) lastMeanBrightness {
  151.     return ((float)(meanRed+meanGreen+meanBlue))/768.0f;
  152. }
  153.  
  154. - (BOOL) copyFromSrc:(unsigned char*)src toDest:(unsigned char*)dst srcRowBytes:(long)srcRB dstRowBytes:(long)dstRB dstBPP:(short)dstBPP {
  155.     int width =MIN(sourceWidth ,destinationWidth );
  156.     int height=MIN(sourceHeight,destinationHeight);
  157.     int srcRowSkip=sourceWidth-width;
  158.     int dstRowSkip=dstRB-width*dstBPP;
  159.     int x,y;
  160.     for (y=0;y<height;y++) {
  161.         for (x=0;x<height;x++) {
  162.             if (dstBPP==4) *(dst++)=255;
  163.             *(dst++)=*src;
  164.             *(dst++)=*src;
  165.             *(dst++)=*(src++);
  166.         }
  167.         src+=srcRowSkip;
  168.         dst+=dstRowSkip;
  169.     }
  170.     return YES;
  171. }
  172.  
  173.  
  174.  
  175. //Do the whole decoding
  176. - (BOOL) convertFromSrc:(unsigned char*)src toDest:(unsigned char*)dst
  177.             srcRowBytes:(long)srcRB dstRowBytes:(long)dstRB dstBPP:(short)dstBPP flip:(BOOL)flip rotate180:(BOOL)rotate180 {
  178.     if (!rgbBuffer) return NO;
  179.     [self demosaicFrom:src type:sourceFormat srcRowBytes:srcRB];
  180.     if (rotate180) [self rotateImage180];
  181.     if (updateGains||produceColorStats) [self calcColorStatistics];
  182.     if (updateGains) [self updateGainsToColorStats];
  183.     [self postprocessGRBGTo:dst dstRowBytes:dstRB dstBPP:dstBPP flip:flip];
  184.     return YES;
  185. }
  186.  
  187. //Internals
  188. - (void) demosaicFrom:(unsigned char*)src type:(short)type srcRowBytes:(long)srcRowBytes {
  189.     /* We expect the Bayer matrix to be in the following format:
  190.  
  191.     G1 R1 G2 R2
  192.     B1 G3 B2 G4
  193.     G5 R3 G6 R4
  194.     B3 G7 B4 G8
  195.  
  196.     -> A GRBG-type Bayer Matrix
  197.     
  198.     and the BGGR type matrix is as follows
  199.     
  200.     B1 G1 B2 G2
  201.     G3 R1 G4 R2
  202.     B3 G5 B4 G6
  203.     G7 R3 G8 R4
  204.  
  205.     RGGB is just rotated...
  206.     
  207.     
  208.     Format 7 - RAW data?
  209.     - each RGB = pixel, thus Grayscale...
  210.     */
  211.     short g1,g2,g3,g4,g5,g6,g7,g8;
  212.     short r1,r2,r3,r4;
  213.     short b1,b2,b3,b4;
  214.     
  215.     unsigned char *green1Run,*green2Run,*green3Run,*green4Run;
  216.     unsigned char *red1Run,*red2Run,*blue1Run,*blue2Run;
  217.     unsigned char *dst1Run,*dst2Run;
  218.     long x,y;
  219.     long dstSkip=sourceWidth*3;
  220.     BOOL GRBGtype = YES;  //  As opposed to BGGR
  221.     
  222.     //source type specific variables 
  223.     long componentStep,srcSkip;
  224.     
  225.     if (type == 7) 
  226.     {
  227.         dst1Run=rgbBuffer;
  228.         
  229.         for (x = 0; x < sourceWidth; x++) 
  230.             for (y = 0; y < sourceHeight; y++) 
  231.             {
  232.                 int val = *src / 2;
  233.                 
  234.                 *(dst1Run++) = val;
  235.                 *(dst1Run++) = val;
  236.                 *(dst1Run++) = val;
  237.                 
  238.                 src++;
  239.             }
  240.         
  241.         return;
  242.     }
  243.     
  244.     switch (type) {
  245.         case 1:    //Components planar in half row, order swapped (STV680-style)
  246.             componentStep=1;
  247.             green1Run =src+sourceWidth/2;
  248.             red1Run   =src;
  249.             blue1Run  =src+srcRowBytes+sourceWidth/2;
  250.             green2Run =src+srcRowBytes;
  251.             break;
  252.         case 2:    //Interleaved data (STV600-style) // GRBG
  253.         case 6: // works like 4 then switch R and B at the end // GBRG
  254.             componentStep=2;
  255.             green1Run =src;
  256.             red1Run   =src+1;
  257.             blue1Run  =src+srcRowBytes;
  258.             green2Run =src+srcRowBytes+1;
  259.             break;
  260.         case 3:    //Row 1: xGxG, Row 2: RBRB (QuickCam Pro subsampled-style)
  261.             componentStep=2;
  262.             red1Run   =src+srcRowBytes+1;
  263.             green1Run =src+1;
  264.             green2Run =src+1;
  265.             blue1Run  =src+srcRowBytes;
  266.             break;
  267.         case 4:    // OV7630 style // BGGR
  268.         case 5: // works like 4 then switch R and B at the end // RGGB
  269.             GRBGtype = NO;
  270.             componentStep=2;
  271.             blue1Run  =src;
  272.             green1Run =src+1;
  273.             green2Run =src+srcRowBytes;
  274.             red1Run   =src+srcRowBytes+1;
  275.             break;
  276.         default: //Assume type 2
  277. #ifdef VERBOSE
  278.             NSLog(@"BayerConverter: Unknown bayer data type: %i",type);
  279. #endif VERBOSE
  280.             componentStep=2;
  281.             green1Run =src;
  282.             red1Run   =src+1;
  283.             blue1Run  =src+srcRowBytes;
  284.             green2Run =src+srcRowBytes+1;
  285.             break;
  286.     }
  287.     
  288.     //init data run pointers
  289.     srcSkip =2*srcRowBytes-(((sourceWidth-2)/2)*componentStep);
  290.     // componentStep is added here to compensate for the initial subtraction in the big loop below
  291.     // the loop over the non-border rows starts with the runs pointing to the left half
  292.     // one could probably eliminate both adding it here and subtracting it later
  293.     green3Run =green1Run+2*srcRowBytes+componentStep;
  294.     red2Run   =red1Run+2*srcRowBytes+componentStep;
  295.     blue2Run  =blue1Run+2*srcRowBytes+componentStep;
  296.     green4Run =green2Run+2*srcRowBytes+componentStep;
  297.     dst1Run=rgbBuffer;
  298.     dst2Run=rgbBuffer+2*dstSkip;
  299.     
  300.     //First row, first column
  301.     *(dst1Run++)=*red1Run;
  302.     *(dst1Run++)=(GRBGtype)?*green1Run:(*green1Run+*green2Run)/2;
  303.     *(dst1Run++)=*blue1Run;
  304.     //First row, non-border columns
  305.     for (x=(sourceWidth-2)/2;x>0;x--) {
  306.         *(dst1Run++)=*red1Run;
  307.         *(dst1Run++)=(GRBGtype)?(*green1Run+*(green1Run+componentStep)+*green2Run)/3:*green1Run;
  308.         *(dst1Run++)=(*blue1Run+*(blue1Run+componentStep))/2;
  309.         if (GRBGtype) green1Run+=componentStep;
  310.         green2Run+=componentStep;
  311.         blue1Run+=componentStep;
  312.         *(dst1Run++)=(*red1Run+*(red1Run+componentStep))/2;
  313.         *(dst1Run++)=(GRBGtype)?*green1Run:(*green1Run+*(green1Run+componentStep)+*green2Run)/3;
  314.         *(dst1Run++)=*blue1Run;
  315.         red1Run+=componentStep;
  316.         if (!GRBGtype) green1Run+=componentStep;
  317.     }
  318.     //First row, last column
  319.     *(dst1Run++)=*red1Run;
  320.     *(dst1Run++)=(GRBGtype)?(*green1Run+*green2Run)/2:*green1Run;
  321.     *(dst1Run++)=*blue1Run;
  322.     //Reset the src data run pointers we changed in the first row - dst1RUn is ok now
  323.     green1Run =green3Run-2*srcRowBytes;
  324.     red1Run   =red2Run-2*srcRowBytes;
  325.     blue1Run  =blue2Run-2*srcRowBytes;
  326.     green2Run =green4Run-2*srcRowBytes;
  327.     
  328.     
  329.     //All non-border rows
  330.     for (y=(sourceHeight-2)/2;y>0;y--) {
  331.         //init right half of colors to left values - will be shifted inside the loop
  332.         r2=*(red1Run-componentStep);    
  333.         r4=*(red2Run-componentStep);
  334.         g2=*(green1Run-componentStep);
  335.         g4=*(green2Run-componentStep);
  336.         g6=*(green3Run-componentStep);
  337.         g8=*(green4Run-componentStep);
  338.         b2=*(blue1Run-componentStep);
  339.         b4=*(blue2Run-componentStep);
  340.         
  341.         //First pixel column in row
  342.         *(dst1Run++)=(GRBGtype)?(r2+r4)/2:r2;
  343.         *(dst1Run++)=(GRBGtype)?(g2+g4+g6)/3:g2;
  344.         *(dst1Run++)=(GRBGtype)?b2:(b2+b4)/2;
  345.         *(dst2Run++)=(GRBGtype)?r4:(r2+r4)/2;
  346.         *(dst2Run++)=(GRBGtype)?g6:(g4+g6+g8)/3;
  347.         *(dst2Run++)=(GRBGtype)?(b2+b4)/2:b4;
  348.         
  349.         //All non-border columns in row
  350.         for (x=(sourceWidth-2)/2;x>0;x--) {
  351.             //shift right half of colors to left half
  352.             r1=r2;
  353.             r3=r4;
  354.             g1=g2;
  355.             g3=g4;
  356.             g5=g6;
  357.             g7=g8;
  358.             b1=b2;
  359.             b3=b4;
  360.             //read new ones
  361.             r2=*red1Run; red1Run+=componentStep;
  362.             g2=*green1Run; green1Run+=componentStep;
  363.             g4=*green2Run; green2Run+=componentStep;
  364.             b2=*blue1Run; blue1Run+=componentStep;
  365.             r4=*red2Run; red2Run+=componentStep;
  366.             g6=*green3Run; green3Run+=componentStep;
  367.             g8=*green4Run; green4Run+=componentStep;
  368.             b4=*blue2Run; blue2Run+=componentStep;
  369.             
  370.             //Interpolate Pixel (2,2): location of g3 (r1).
  371.             *(dst1Run++)=(GRBGtype)?(r1+r3)/2:r1;
  372.             *(dst1Run++)=(GRBGtype)?g3:(g1+g3+g4+g5)/4;
  373.             *(dst1Run++)=(GRBGtype)?(b1+b2)/2:(b1+b2+b3+b4)/4;
  374.             //Interpolate Pixel (3,2): location of b2 (g4).
  375.             *(dst1Run++)=(GRBGtype)?(r1+r2+r3+r4)/4:(r1+r2)/2;
  376.             *(dst1Run++)=(GRBGtype)?(g2+g3+g4+g6)/4:g4;
  377.             *(dst1Run++)=(GRBGtype)?b2:(b2+b4)/2;
  378.             //Interpolate Pixel (2,3): location of r3 (g5).
  379.             *(dst2Run++)=(GRBGtype)?r3:(r1+r3)/2;
  380.             *(dst2Run++)=(GRBGtype)?(g3+g5+g6+g7)/4:g5;
  381.             *(dst2Run++)=(GRBGtype)?(b1+b2+b3+b4)/4:(b3+b4)/2;
  382.             //Interpolate Pixel (3,3): location of g6 (b4).
  383.             *(dst2Run++)=(GRBGtype)?(r3+r4)/2:(r1+r2+r3+r4)/4;
  384.             *(dst2Run++)=(GRBGtype)?g6:(g4+g5+g6+g8)/4;
  385.             *(dst2Run++)=(GRBGtype)?(b2+b4)/2:b4;
  386.         }
  387.         
  388.         //last pixel column in row
  389.         *(dst1Run++)=(GRBGtype)?(r2+r4)/2:r2;
  390.         *(dst1Run++)=(GRBGtype)?g4:(g2+g4+g6)/3;
  391.         *(dst1Run++)=(GRBGtype)?b2:(b2+b4)/2;
  392.         *(dst2Run++)=(GRBGtype)?r4:(r2+r4)/2;
  393.         *(dst2Run++)=(GRBGtype)?(g4+g6+g8)/3:g6;
  394.         *(dst2Run++)=(GRBGtype)?(b2+b4)/2:b4;
  395.         
  396.         //go to start of next two lines
  397.         dst1Run+=dstSkip;
  398.         dst2Run+=dstSkip;
  399.         red1Run+=srcSkip;
  400.         red2Run+=srcSkip;
  401.         green1Run+=srcSkip;
  402.         green2Run+=srcSkip;
  403.         green3Run+=srcSkip;
  404.         green4Run+=srcSkip;
  405.         blue1Run+=srcSkip;
  406.         blue2Run+=srcSkip;
  407.     }
  408.     // corrections
  409.     red1Run   += srcRowBytes - srcSkip;
  410.     red2Run   += srcRowBytes - srcSkip;
  411.     green1Run += srcRowBytes - srcSkip;
  412.     green2Run += srcRowBytes - srcSkip;
  413.     green3Run += srcRowBytes - srcSkip;
  414.     green4Run += srcRowBytes - srcSkip;
  415.     blue1Run  += srcRowBytes - srcSkip;
  416.     blue2Run  += srcRowBytes - srcSkip;
  417.     //Last row, first column
  418.     *(dst1Run++)=*red1Run;
  419.     *(dst1Run++)=(GRBGtype)?(*green1Run+*green2Run)/2:*green1Run;
  420.     *(dst1Run++)=*blue1Run;
  421.     //Last row, non-border columns
  422.     for (x=(sourceWidth-2)/2;x>0;x--) {
  423.         *(dst1Run++)=*red1Run;
  424.         *(dst1Run++)=(GRBGtype)?*green2Run:(*green1Run+*green2Run+*(green2Run+componentStep))/3;
  425.         *(dst1Run++)=(*blue1Run+*(blue1Run+componentStep))/2;
  426.         green1Run+=componentStep;
  427.         blue1Run+=componentStep;
  428.         *(dst1Run++)=(*red1Run+*(red1Run+componentStep))/2;
  429.         *(dst1Run++)=(GRBGtype)?(*green1Run+*green2Run+*(green2Run+componentStep))/3:*(green2Run+componentStep);
  430.         *(dst1Run++)=*blue1Run;
  431.         red1Run+=componentStep;
  432.         green2Run+=componentStep;
  433.     }
  434.     //Last row, last column
  435.     *(dst1Run++)=*red1Run;
  436.     *(dst1Run++)=(GRBGtype)?*green2Run:(*green1Run+*green2Run)/2;
  437.     *(dst1Run++)=*blue1Run;
  438.     
  439.     if (type == 5 || type == 6) // RGGB or GBRG
  440.     {
  441.         for (y = 0; y < sourceHeight; y++) 
  442.             for (x = 0; x < sourceWidth; x++) 
  443.             {
  444.                 unsigned char temp = rgbBuffer[3 * (x + y * sourceWidth) + 0]; // R
  445.                 rgbBuffer[3 * (x + y * sourceWidth) + 0] = rgbBuffer[3 * (x + y * sourceWidth) + 2];
  446.                 rgbBuffer[3 * (x + y * sourceWidth) + 2] = temp;
  447.             }
  448.     }
  449. }
  450.  
  451. #define _SL (long)
  452. #define _B1 0xff000000
  453. #define _B2 0x00ff0000
  454. #define _B3 0x0000ff00
  455. #define _B4 0x000000ff
  456.  
  457. //The following macro applies saturation, brightness, contrast and gamma to a rgb triple
  458. #define COLORPROCESS(r,g,b) {\
  459.     r=(((r-g)*saturation)/65536)+g;\
  460.         b=(((b-g)*saturation)/65536)+g;\
  461.             r=redTransferLookup[CLAMP(r,0,255)];\
  462.                 g=greenTransferLookup[CLAMP(g,0,255)];\
  463.                     b=blueTransferLookup[CLAMP(b,0,255)];\
  464. }
  465.  
  466. #define NOCOLORPROCESS(r,g,b) {\
  467.             r=CLAMP(r,0,255);\
  468.                 g=CLAMP(g,0,255);\
  469.                     b=CLAMP(b,0,255);\
  470. }
  471.  
  472. - (void) postprocessGRBGTo:(unsigned char*)dst dstRowBytes:(long)dstRB dstBPP:(short)dstBPP flip:(BOOL)flip{
  473. /* Does someone have a good idea how to do some speed optimizations in here? */
  474.     unsigned char* src1Run=rgbBuffer;
  475.     unsigned char* src2Run=rgbBuffer+3*sourceWidth;
  476.     unsigned char* src3Run=rgbBuffer+6*sourceWidth;
  477.     unsigned char* src4Run=rgbBuffer+9*sourceWidth;
  478.     long sharpen=(long)(sharpness*65536.0f);        //fixed-point 17:15 factor for sharpening - 0.5 produces standard sharpen
  479.     long x,y;
  480.     long r1,g1,b1,r2,g2,b2,r3,g3,b3,r4,g4,b4;
  481.     unsigned long r1c1,r1c2,r1c3,r2c1,r2c2,r2c3,r3c1,r3c2,r3c3,r4c1,r4c2,r4c3; //Row, column
  482.  
  483.     long width=MIN(sourceWidth-2,destinationWidth);    //Find out the real (inner) blit size
  484.     long height=MIN(sourceHeight-2,destinationHeight);
  485.  
  486.     BOOL leftBorder=(destinationWidth>width);        //Find which borders we need to blit (without sharpening)
  487.     BOOL rightBorder=(destinationWidth>(width+1));    //Note that when rightBorder is YES, we can also expect leftBorder
  488.     BOOL topBorder=(destinationHeight>height);
  489.     BOOL bottomBorder=(destinationHeight>(height+1));    //Note that when bottomBorder is YES, we can also expect topBorder
  490.  
  491.     unsigned char* dst1Run=dst+((leftBorder)?3:0)+((topBorder)?dstRB:0);
  492.     unsigned char* dst2Run=dst1Run+dstRB;
  493.  
  494.     long srcSkip=6*sourceWidth-3*width;            //Skip two lines of source minus the bytes we add internally
  495.     long dstSkip=2*dstRB-width*dstBPP;            //Skip two lines of destination minus the bytes we add internally
  496.  
  497.     short writeMode=dstBPP;                //To distinguish the way the pixels are written
  498.     if (flip) {
  499.         writeMode+=256;
  500.         dstSkip+=2*dstBPP*width;
  501.         dst1Run=dst+((rightBorder)?3:0)+((topBorder)?dstRB:0)+width*dstBPP;
  502.         dst2Run=dst1Run+dstRB;
  503.     }
  504.     
  505. //The following two nested loops do the postprocessing for all non-border pixels. Borders follow afterwards
  506.     for (y=height/2;y>0;y--) {
  507.         for (x=width/2;x>0;x--) {
  508. //Step 1: Read matix data from memory to rxcx variables and update source pointers for next iteration
  509.             r1c1=CFSwapInt32HostToBig(*((unsigned long*)(src1Run)));
  510.             r1c2=CFSwapInt32HostToBig(*((unsigned long*)(src1Run+4)));
  511.             r1c3=CFSwapInt32HostToBig(*((unsigned long*)(src1Run+8)));
  512.             r2c1=CFSwapInt32HostToBig(*((unsigned long*)(src2Run)));
  513.             r2c2=CFSwapInt32HostToBig(*((unsigned long*)(src2Run+4)));
  514.             r2c3=CFSwapInt32HostToBig(*((unsigned long*)(src2Run+8)));
  515.             r3c1=CFSwapInt32HostToBig(*((unsigned long*)(src3Run)));
  516.             r3c2=CFSwapInt32HostToBig(*((unsigned long*)(src3Run+4)));
  517.             r3c3=CFSwapInt32HostToBig(*((unsigned long*)(src3Run+8)));
  518.             r4c1=CFSwapInt32HostToBig(*((unsigned long*)(src4Run)));
  519.             r4c2=CFSwapInt32HostToBig(*((unsigned long*)(src4Run+4)));
  520.             r4c3=CFSwapInt32HostToBig(*((unsigned long*)(src4Run+8)));
  521.             src1Run+=6;
  522.             src2Run+=6;
  523.             src3Run+=6;
  524.             src4Run+=6;
  525. /* Step 2: Sharpen. There are many known algorithms that do this task. My first approach was to apply a 3x3 sharpen filter matrix to each component. This does an average sharpening job but introduces some artefacts (some pixels are sharpened too much, some not enough). The secnd approach was to use a different sharpening matrix for each component - based on their interpolation type. Also a bad idea. This is the third approach (it's so simple and obvious that it must habe been invented by someone else before - sorry, I'm too lazy right now to look up the name). The plot is as follows: 
  526.  
  527. The primary assumption is that resolution in luminance is more important in human reception than chrominance and that in natural images, there is less chrominance structure than luminance. This is especially important for edges: Humans detect edges primarily by luminance. In natural images, borders of differently colored areas are in most cases also accompanied by a change of luminance.
  528.  
  529. We have produced a linear interpolation in the step before. This is ok for the low frequencies but the high ones are missing. Because of  the linear interpolation, each component (R,G or B) plane may have "peaks" at the points where there was a same-colored sample in the Bayer matrix (of course, there are only peaks where the samples form no linear plane). We assume the pixel components at those peaks to be already correct (because they come directly from the Bayer matrix - not really correct but simpler...) - the other ones have to pe corrected (by adding their high frequency part). There is no way to find out about the high frequencies when we look at the component planes individually - we are below the Nyquist Rate and following the Samling Theorem, there is no way to get that information back (even if we allow introducing aliasing artefacts). But this is the point where our assumption comes into play: Low chrominance means that the components are similar. The idea is to "steal the peak" from another component plane. For every pixel location, there is one color component that has a peak (beacuse of the Bayer matrix samples every location in a certain color). "Stealing the peak" means to calculate the nonlinearity of one plane at a given point and apply this value to another plane at the same point. How the nonlinearity is calculated depends on the interpolation type that was originally used in the linear interpolation step for the pixel component we want to enhance (we take the same shape).
  530.  
  531.  Of course, this algorithm will also introduce artefacts (there's no way to avoid it if we want to have higher frequencies than those by produced by an "ordinary" interpolation - introducing high frequencies is pure guessing here). But the algorithm introduces almost no artefacts on unicolored areas, soft gradients, and luminance edges. The artefacts introduced are typically on edges with high chrominance change in comparison to luminance. This is something that rarely happens in natural images. I was surprised about the good results. It does everything better than the one before: "Real" high frequencies, less aliasing, less other artefacts, and the main reason: It's faster (some optimizations could still be made, but it should be fast enough for using one camera on every OSX-class machines - G3/233 and up). Another reason to do so: The driver knows about the Bayer matrix and is supposed to give back the best the camera can give. Ordinary sharpening with filter matrices can be done later in a image-processing application of your choice...
  532.  
  533. Don't take me wrong - this is not the best postprocessing that could be done. But in a live video environment, we don't have much choice...
  534.  
  535. */
  536.             //Pixel (1,1) red: Pipe, steal from green
  537.             r1=_SL(r2c1&_B4)
  538.                 +(((_SL((r2c2&_B1)>>23)-_SL((r1c2&_B1)>>24)-_SL((r3c2&_B1)>>24))*sharpen)/65536);
  539.             //Pixel (1,1) green: Dot
  540.             g1=_SL((r2c2&_B1)>>24);
  541.             //Pixel (1,1) blue: Minus, steal from green
  542.             b1=_SL((r2c2&_B2)>>16)
  543.                 +(((_SL((r2c2&_B1)>>23)-_SL((r2c1&_B2)>>16)-_SL(r2c2&_B4))*sharpen)/65536);
  544.             //Pixel (2,1) red: X, steal from blue
  545.             r2=_SL((r2c2&_B3)>>8)
  546.                 +(((_SL((r2c3&_B1)>>22)-_SL((r1c2&_B2)>>16)-_SL(r1c3&_B4)-_SL((r3c2&_B2)>>16)-_SL(r3c3&_B4))*sharpen)/131072);
  547.             //Pixel (2,1) green: Plus, steal from blue
  548.             g2=_SL(r2c2&_B4)
  549.                 +(((_SL((r2c3&_B1)>>22)-_SL((r1c3&_B1)>>24)-_SL((r2c2&_B2)>>16)-_SL(r2c3&_B4)-_SL((r3c3&_B1)>>24))*sharpen)/131072);
  550.             //Pixel (2,1) blue: Dot
  551.             b2=_SL((r2c3&_B1)>>24);
  552.             //Pixel (1,2) red: Dot
  553.             r3=_SL(r3c1&_B4);
  554.             //Pixel (1,2) green: Plus, steal from red
  555.             g3=_SL((r3c2&_B1)>>24)
  556.                 +(((_SL((r3c1&_B4)<<2)-_SL(r2c1&_B4)-_SL((r3c1&_B1)>>24)-_SL((r3c2&_B3)>>8)-_SL(r4c1&_B4))*sharpen)/131072);
  557.             //Pixel (1,2) blue: X, steal from red
  558.             b3=_SL((r3c2&_B2)>>16)
  559.                 +(((_SL((r3c1&_B4)<<2)-_SL((r2c1&_B1)>>24)-_SL((r2c2&_B3)>>8)-_SL((r4c1&_B1)>>24)-_SL((r4c2&_B3)>>8))*sharpen)/131072);
  560.             //Pixel (2,2) red: Minus, steal from green
  561.             r4=_SL((r3c2&_B3)>>8)
  562.                 +(((_SL((r3c2&_B4)<<1)-_SL((r3c2&_B1)>>24)-_SL((r3c3&_B3)>>8))*sharpen)/65536);
  563.             //Pixel (2,2) green: Dot
  564.             g4=_SL(r3c2&_B4);
  565.             //Pixel (2,2) blue: Pipe, steal from green
  566.             b4=_SL((r3c3&_B1)>>24)
  567.                 +(((_SL((r3c2&_B4)<<1)-_SL(r2c2&_B4)-_SL(r4c2&_B4))*sharpen)/65536);
  568.  
  569.             //Step 3: Apply color adjustments
  570.             if (needsTransferLookup) {
  571.                 COLORPROCESS(r1,g1,b1);
  572.                 COLORPROCESS(r2,g2,b2);
  573.                 COLORPROCESS(r3,g3,b3);
  574.                 COLORPROCESS(r4,g4,b4);
  575.             } else {
  576.                 NOCOLORPROCESS(r1,g1,b1);
  577.                 NOCOLORPROCESS(r2,g2,b2);
  578.                 NOCOLORPROCESS(r3,g3,b3);
  579.                 NOCOLORPROCESS(r4,g4,b4);
  580.             }                
  581.             //Step 4: Assemble values and write to destination, update destination pointers
  582.             switch (writeMode) {
  583.                 case 3:
  584.                     *((unsigned long* )(dst1Run  ))=CFSwapInt32BigToHost((r1<<24)+(g1<<16)+(b1<<8)+r2);
  585.                     *((unsigned short*)(dst1Run+4))=CFSwapInt16BigToHost(                  (g2<<8)+b2);
  586.                     *((unsigned long* )(dst2Run  ))=CFSwapInt32BigToHost((r3<<24)+(g3<<16)+(b3<<8)+r4);
  587.                     *((unsigned short*)(dst2Run+4))=CFSwapInt16BigToHost(                  (g4<<8)+b4);
  588.                     dst1Run+=6;
  589.                     dst2Run+=6;
  590.                     break;
  591.                 case 4:
  592.                     *((unsigned long*)(dst1Run  ))=CFSwapInt32BigToHost(0xff000000+(r1<<16)+(g1<<8)+(b1));
  593.                     *((unsigned long*)(dst1Run+4))=CFSwapInt32BigToHost(0xff000000+(r2<<16)+(g2<<8)+(b2));
  594.                     *((unsigned long*)(dst2Run  ))=CFSwapInt32BigToHost(0xff000000+(r3<<16)+(g3<<8)+(b3));
  595.                     *((unsigned long*)(dst2Run+4))=CFSwapInt32BigToHost(0xff000000+(r4<<16)+(g4<<8)+(b4));
  596.                     dst1Run+=8;
  597.                     dst2Run+=8;
  598.                     break;
  599.                 case 259:
  600.                     dst1Run-=6;
  601.                     dst2Run-=6;
  602.                     *((unsigned long* )(dst1Run  ))=CFSwapInt32BigToHost((r2<<24)+(g2<<16)+(b2<<8)+r1);
  603.                     *((unsigned short*)(dst1Run+4))=CFSwapInt16BigToHost(                  (g1<<8)+b1);
  604.                     *((unsigned long* )(dst2Run  ))=CFSwapInt32BigToHost((r4<<24)+(g4<<16)+(b4<<8)+r3);
  605.                     *((unsigned short*)(dst2Run+4))=CFSwapInt16BigToHost(                  (g3<<8)+b3);
  606.                     break;
  607.                 case 260:
  608.                     dst1Run-=8;
  609.                     dst2Run-=8;
  610.                     *((unsigned long*)(dst1Run  ))=CFSwapInt32BigToHost(0xff000000+(r2<<16)+(g2<<8)+(b2));
  611.                     *((unsigned long*)(dst1Run+4))=CFSwapInt32BigToHost(0xff000000+(r1<<16)+(g1<<8)+(b1));
  612.                     *((unsigned long*)(dst2Run  ))=CFSwapInt32BigToHost(0xff000000+(r4<<16)+(g4<<8)+(b4));
  613.                     *((unsigned long*)(dst2Run+4))=CFSwapInt32BigToHost(0xff000000+(r3<<16)+(g3<<8)+(b3));
  614.                     break;
  615.             }
  616.         }
  617.         src1Run+=srcSkip;
  618.         src2Run+=srcSkip;
  619.         src3Run+=srcSkip;
  620.         src4Run+=srcSkip;
  621.         dst1Run+=dstSkip;
  622.         dst2Run+=dstSkip;
  623.     }
  624.  
  625.     //All inner pixels are done now. If we need to use borders as well, do it now. Some sensors give us additional borders to interpolate, others do not...
  626.     if (topBorder) {
  627.         int topBorderWidth=width+((leftBorder)?1:0);
  628.         src1Run=rgbBuffer;
  629.         dst1Run=dst;
  630.         if (flip) dst1Run+=topBorderWidth*dstBPP;
  631.         for (x=topBorderWidth;x>0;x--) {
  632.             r1=*(src1Run++);
  633.             g1=*(src1Run++);
  634.             b1=*(src1Run++);
  635.             COLORPROCESS(r1,g1,b1);
  636.             switch (writeMode) {
  637.                 case 3:
  638.                     *(dst1Run++)=r1;
  639.                     *(dst1Run++)=g1;
  640.                     *(dst1Run++)=b1;
  641.                     break;
  642.                 case 4:
  643.                     *(dst1Run++)=0xff;
  644.                     *(dst1Run++)=r1;
  645.                     *(dst1Run++)=g1;
  646.                     *(dst1Run++)=b1;
  647.                 case 259:
  648.                     *(--dst1Run)=b1;
  649.                     *(--dst1Run)=g1;
  650.                     *(--dst1Run)=r1;
  651.                     break;
  652.                 case 260:
  653.                     *(--dst1Run)=b1;
  654.                     *(--dst1Run)=g1;
  655.                     *(--dst1Run)=r1;
  656.                     *(--dst1Run)=0xff;
  657.                     break;
  658.             }
  659.         }
  660.     }
  661.     if (leftBorder) {
  662.         src1Run=rgbBuffer+((topBorder)?0:(sourceWidth*3));
  663.         dst1Run=dst;
  664.         if (flip) dst1Run+=(sourceWidth-1)*dstBPP;    //Flip? -> Move left to right border
  665.         for (y=height+((topBorder)?1:0);y>0;y--) {
  666.             r1=src1Run[0];
  667.             g1=src1Run[1];
  668.             b1=src1Run[2];
  669.             COLORPROCESS(r1,g1,b1);
  670.             if (dstBPP==4) {
  671.                 dst1Run[0]=0xff;
  672.                 dst1Run[1]=r1;
  673.                 dst1Run[2]=g1;
  674.                 dst1Run[3]=b1;
  675.             } else {
  676.                 dst1Run[0]=r1;
  677.                 dst1Run[1]=g1;
  678.                 dst1Run[2]=b1;
  679.             }
  680.             src1Run+=sourceWidth*3;
  681.             dst1Run+=dstRB;
  682.         }
  683.     }
  684.     if (bottomBorder) {
  685.         int bottomBorderWidth=width+((leftBorder)?1:0)+((rightBorder)?1:0);
  686.         src1Run=rgbBuffer+(sourceHeight-1)*(sourceWidth*3);    //Last line in rgbBuffer
  687.         dst1Run=dst+(sourceHeight-1)*dstRB;            //Last line in dest buffer
  688.         if (flip) dst1Run+=bottomBorderWidth*dstBPP;
  689.         for (x=bottomBorderWidth;x>0;x--) {
  690.             r1=*(src1Run++);
  691.             g1=*(src1Run++);
  692.             b1=*(src1Run++);
  693.             COLORPROCESS(r1,g1,b1);
  694.             switch (writeMode) {
  695.                 case 3:
  696.                     *(dst1Run++)=r1;
  697.                     *(dst1Run++)=g1;
  698.                     *(dst1Run++)=b1;
  699.                     break;
  700.                 case 4:
  701.                     *(dst1Run++)=0xff;
  702.                     *(dst1Run++)=r1;
  703.                     *(dst1Run++)=g1;
  704.                     *(dst1Run++)=b1;
  705.                 case 259:
  706.                     *(--dst1Run)=b1;
  707.                     *(--dst1Run)=g1;
  708.                     *(--dst1Run)=r1;
  709.                     break;
  710.                 case 260:
  711.                     *(--dst1Run)=b1;
  712.                     *(--dst1Run)=g1;
  713.                     *(--dst1Run)=r1;
  714.                     *(--dst1Run)=0xff;
  715.                     break;
  716.             }
  717.         }
  718.     }
  719.     if (rightBorder) {
  720.         src1Run=rgbBuffer+(sourceWidth-1)*3;            //Last column in rgbBuffer
  721.         dst1Run=dst+(sourceWidth-1)*dstBPP;            //Last column in dset buffer
  722.         if (flip) dst1Run-=(sourceWidth-1)*dstBPP;        //Flip? -> move right to left border
  723.         for (y=height+((topBorder)?1:0)+((bottomBorder)?1:0);y>0;y--) {
  724.             r1=src1Run[0];
  725.             g1=src1Run[1];
  726.             b1=src1Run[2];
  727.             COLORPROCESS(r1,g1,b1);
  728.             if (dstBPP==4) {
  729.                 dst1Run[0]=0xff;
  730.                 dst1Run[1]=r1;
  731.                 dst1Run[2]=g1;
  732.                 dst1Run[3]=b1;
  733.             } else {
  734.                 dst1Run[0]=r1;
  735.                 dst1Run[1]=g1;
  736.                 dst1Run[2]=b1;
  737.             }
  738.             src1Run+=sourceWidth*3;
  739.             dst1Run+=dstRB;
  740.         }
  741.     }
  742. }    
  743.  
  744. /*
  745.  
  746.  This method will only be called when the rgb (temporary) buffer is filled. 
  747.  
  748.  */
  749.  
  750. - (void) rotateImage180 {
  751.     long x, y, z;
  752.     long width = sourceWidth;
  753.     long height = sourceHeight;
  754.     unsigned char temp[3];
  755.     unsigned char * buffer = rgbBuffer;
  756.     
  757.     if (buffer == NULL) 
  758.         return;
  759.     
  760.     for (y = 0; y < (height+1)/2; y++) 
  761.         for (x = 0; x < width; x++) 
  762.             for (z = 0; z < 3; z++) 
  763.             {
  764.                 temp[z] = rgbBuffer[3 * (y * width + x) + z];
  765.                 rgbBuffer[3 * (y * width + x) + z] = rgbBuffer[3 * ((height - y) * width - x - 1) + z];
  766.                 rgbBuffer[3 * ((height - y) * width - x - 1) + z] = temp[z];
  767.             }
  768. }
  769.  
  770.  
  771. - (void) flipImageHorizontal {
  772.     long x, y, z;
  773.     long width = sourceWidth;
  774.     long height = sourceHeight;
  775.     unsigned char temp[3];
  776.     unsigned char * buffer = rgbBuffer;
  777.     
  778.     if (buffer == NULL) 
  779.         return;
  780.     
  781.     for (y = 0; y < height; y++) 
  782.         for (x = 0; x < (width)/2; x++) 
  783.             for (z = 0; z < 3; z++) 
  784.             {
  785.                 temp[z] = rgbBuffer[3 * (y * width + x) + z];
  786.                 rgbBuffer[3 * (y * width + x) + z] = rgbBuffer[3 * (y * width + (width - x - 1)) + z];
  787.                 rgbBuffer[3 * (y * width + (width - x - 1)) + z] = temp[z];
  788.             }
  789. }
  790.  
  791. /*
  792.  
  793.  This method will only be called when the rgb (temporary) buffer is filled. We take the average color of the temp image by adding some sample pixels from the temp image. Its color value is equal to the raw image. It's unsharp, but that doesn't matter... If the existing averages were valid, we update the averages (they are an exponentional average sum), if not, we set them. Then, we calculate compensation gains for the average color (which should sum up to three).
  794.  
  795. */
  796.  
  797. #define STATISTICS_SAMPLE_STEP 5    //take every fifth pixel - it won't be a regular grid and it's faster...
  798. - (void) calcColorStatistics {
  799.     unsigned char* run;
  800.     unsigned char* max=rgbBuffer+3*sourceWidth*sourceHeight;
  801.     unsigned long redSum=0;
  802.     unsigned long greenSum=0;
  803.     unsigned long blueSum=0;
  804.     for (run=rgbBuffer;run<max;run+=3*STATISTICS_SAMPLE_STEP) {
  805.         redSum+=*run;
  806.         greenSum+=run[1];
  807.         blueSum+=run[2];
  808.     }
  809.     meanRed=((float)(redSum))/((float)(sourceWidth*sourceHeight)/(float)STATISTICS_SAMPLE_STEP);
  810.     meanGreen=((float)(greenSum))/((float)(sourceWidth*sourceHeight)/(float)STATISTICS_SAMPLE_STEP);
  811.     meanBlue=((float)(blueSum))/((float)(sourceWidth*sourceHeight)/(float)STATISTICS_SAMPLE_STEP);
  812.     meanRed=MAX(1.0f,meanRed);
  813.     meanGreen=MAX(1.0f,meanGreen);
  814.     meanBlue=MAX(1.0f,meanBlue);
  815. }
  816.  
  817. #define AVG_SUM_EXP 0.95 //The averaging factor (0..1( - higher value means slower reaction to color changes 
  818. - (void) updateGainsToColorStats {
  819.     float averageSumFactor=(1.0f/(1.0f-AVG_SUM_EXP));
  820.     float scaleFactor;
  821.  
  822.     scaleFactor=3.0f/(meanRed+meanGreen+meanBlue);//Scale (r,g,b) to sum up to the same as the unit color (1,1,1). 
  823.  
  824. //Update/set to averages
  825.     if (averageSumsValid) {
  826.         averageRedSum=averageRedSum*AVG_SUM_EXP+meanRed*scaleFactor;
  827.         averageGreenSum=averageGreenSum*AVG_SUM_EXP+meanGreen*scaleFactor;
  828.         averageBlueSum=averageBlueSum*AVG_SUM_EXP+meanBlue*scaleFactor;
  829.     } else {
  830.         averageRedSum=meanRed*scaleFactor*averageSumFactor;
  831.         averageGreenSum=meanGreen*scaleFactor*averageSumFactor;
  832.         averageBlueSum=meanBlue*scaleFactor*averageSumFactor;
  833.         averageSumsValid=YES;
  834.     }
  835.  
  836. /* Average sums are now ok - they represent averageFactor times the mean color scaled to be about equally bright to (1,1,1) ("almost" because the individual  components are unweighted). Now calculate the gains. Note that this method is not good yet - it produces quite high gains for extreme colors - mathematically, this is correct - it makes the average gray. But it doesn't look too good... Maybe a nonlinear reduction would be fine... Maybe only brighter colors should be taken into account... Maybe I should read a bit about color correction... This solution is an ad hoc hack. */
  837.  
  838.     redGain=1.0f/(averageRedSum/averageSumFactor);
  839.     greenGain=1.0f/(averageGreenSum/averageSumFactor);
  840.     blueGain=1.0f/(averageBlueSum/averageSumFactor);
  841. //    NSLog(@"auto gains: %f %f %f",redGain,greenGain,blueGain);
  842.     [self recalcTransferLookup];
  843. }
  844.  
  845. - (void) recalcTransferLookup {
  846.     float f,r,g,b;
  847.     short i;
  848.     float sat=((float)saturation)/65536.0f;
  849.     for (i=0;i<256;i++) {
  850.         f=((float)i)/255;
  851.         f=pow(f,gamma);                    //Bend to gamma
  852.         f+=brightness;                    //Offset brightness
  853.         f=((f-0.5f)*contrast)+0.5f;            //Scale around 0.5
  854.         f*=255.0f;                    //Scale to [0..255]
  855.         r=f*(sat*redGain+(1.0f-sat));            //Scale to red gain (itself scaled by saturation)
  856.         g=f*(sat*greenGain+(1.0f-sat));            //Scale to green gain (itself scaled by saturation)
  857.         b=f*(sat*blueGain+(1.0f-sat));            //Scale to blue gain (itself scaled by saturation)
  858.         redTransferLookup[i]=CLAMP(r,0.0f,255.0f);    //Clamp and set
  859.         greenTransferLookup[i]=CLAMP(g,0.0f,255.0f);    //Clamp and set
  860.         blueTransferLookup[i]=CLAMP(b,0.0f,255.0f);;    //Clamp and set
  861.     }
  862.     needsTransferLookup=(gamma!=1.0f)||(brightness!=0.0f)||(contrast!=1.0f)
  863.         ||(saturation!=65536)||(redGain!=1.0f)||(greenGain!=1.0f)||(blueGain!=1.0f);
  864. }
  865.  
  866. @end
  867.  
  868.  
  869. @interface CyYeGMgConverter (Private)
  870.  
  871. - (void) demosaicFrom:(unsigned char*)src type:(short)type srcRowBytes:(long)srcRowBytes;
  872.  
  873. @end
  874.  
  875.  
  876. @implementation CyYeGMgConverter
  877.  
  878.  
  879. - (void) setSourceFormat: (short) fmt
  880. {
  881.     if ((fmt < 1) || (fmt > 8)) 
  882.         return;
  883.     sourceFormat = fmt;
  884. }
  885.  
  886.  
  887. //Internals
  888. - (void) demosaicFrom: (unsigned char *) src type: (short) type srcRowBytes: (long) srcRowBytes 
  889. {
  890. /*
  891.     Format 8 - CMYG??
  892.     
  893.     Cy  Ye  Cy  Ye  Cy  Ye ...
  894.     G   Mg  G   Mg  G   Mg ...
  895.     Cy  Ye  Cy  Ye  Cy  Ye ...
  896.     Mg  G   Mg  G   Mg  G  ...
  897.     Cy  Ye  Cy  Ye  Cy  Ye ...
  898.     G   Mg  G   Mg  G   Mg ...
  899.     Cy  Ye  Cy  Ye  Cy  Ye ...
  900.     Mg  G   Mg  G   Mg  G  ...
  901.     .
  902.     .
  903.     .
  904.     
  905. */
  906.     /*
  907.     short g1,g2,g3,g4,g5,g6,g7,g8;
  908.     short r1,r2,r3,r4;
  909.     short b1,b2,b3,b4;
  910.     
  911.     unsigned char *green1Run,*green2Run,*green3Run,*green4Run;
  912.     unsigned char *red1Run,*red2Run,*blue1Run,*blue2Run;
  913.     unsigned char *dst1Run,*dst2Run;
  914.     long dstSkip=sourceWidth*3;
  915.     BOOL GRBGtype = YES;  //  As opposed to BGGR
  916.     BOOL CyYeGrMgtype = NO;
  917.     
  918.     // every other pixel is the same type of component
  919.     long componentStep = 2;
  920.     // 
  921.     long srcSkip = 2 * srcRowBytes - (sourceWidth / 2) * componentStep;
  922. */
  923.     int Cy, Ye, Gr, Mg, R, G, B;
  924.     int x, y, alternate;
  925.     
  926.     if (type <= MAX_BAYER_TYPE) 
  927.         [super demosaicFrom:src type:type srcRowBytes:srcRowBytes];
  928.     
  929. //    printf("Processing CyYeGMg pattern now!\n");
  930.     
  931.     for (y = 0; y < sourceHeight; y += 2) 
  932.         for (x = 0; x < sourceWidth; x+= 2) 
  933.         {
  934.             alternate = (y % 4 == 0) ? 0 : 1;
  935.             
  936.             Cy = src[(y + 0) * srcRowBytes + (x + 0)];
  937.             Ye = src[(y + 0) * srcRowBytes + (x + 1)];
  938.             Gr = src[(y + 1) * srcRowBytes + (x + 0 + alternate)];
  939.             Mg = src[(y + 1) * srcRowBytes + (x + 1 - alternate)];
  940.             
  941.             R = Mg + Ye - Cy;
  942.             G = Ye + Cy - Mg;
  943.             B = Cy + Mg - Ye;
  944.             
  945.             // G should be close to Gr
  946.             
  947.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 0)) + 0] = CLAMP(R,0,255);
  948.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 0)) + 1] = CLAMP(G,0,255);
  949.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 0)) + 2] = CLAMP(B,0,255);
  950.             
  951.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 1)) + 0] = CLAMP(R,0,255);
  952.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 1)) + 1] = CLAMP(G,0,255);
  953.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 1)) + 2] = CLAMP(B,0,255);
  954.             
  955.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 0)) + 0] = CLAMP(R,0,255);
  956.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 0 + alternate)) + 1] = CLAMP(Gr,0,255); // G should be close to Gr
  957.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 0)) + 2] = CLAMP(B,0,255);
  958.             
  959.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 1)) + 0] = CLAMP(R,0,255);
  960.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 1 - alternate)) + 1] = CLAMP(G,0,255);
  961.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 1)) + 2] = CLAMP(B,0,255);
  962.  
  963. #if 1
  964.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 0)) + 0] = CLAMP(0,0,255);
  965.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 0)) + 1] = CLAMP(Cy,0,255);
  966.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 0)) + 2] = CLAMP(Cy,0,255);
  967.             
  968.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 1)) + 0] = CLAMP(Ye,0,255);
  969.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 1)) + 1] = CLAMP(Ye,0,255);
  970.             rgbBuffer[3 * (((y + 0) * sourceWidth) + (x + 1)) + 2] = CLAMP(0,0,255);
  971.             
  972.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 0 + alternate)) + 0] = CLAMP(0,0,255);
  973.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 0 + alternate)) + 1] = CLAMP(Gr,0,255); // G should be close to Gr
  974.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 0 + alternate)) + 2] = CLAMP(0,0,255);
  975.             
  976.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 1 - alternate)) + 0] = CLAMP(Mg,0,255);
  977.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 1 - alternate)) + 1] = CLAMP(0,0,255);
  978.             rgbBuffer[3 * (((y + 1) * sourceWidth) + (x + 1 - alternate)) + 2] = CLAMP(Mg,0,255);
  979. #endif
  980.         }
  981.         
  982.     
  983. /*
  984.     // corners
  985.     
  986.     // TL
  987.     // TR
  988.     // BL
  989.     // BR
  990.     
  991.     // top & bottom
  992.     
  993.     for (x = 1; x < sourceWidth - 1; x++) 
  994.     {
  995.         // TOP
  996.         // BOTTOM
  997.     }
  998.     
  999.     // sides
  1000.     
  1001.     for (y = 1; y < sourceHeight - 1; y++) 
  1002.     {
  1003.         // LEFT
  1004.         // RIGHT
  1005.     }
  1006.     
  1007.     // middle
  1008.     
  1009.     for (x = 1; x < sourceWidth - 1; x++) 
  1010.     {
  1011.         for (y = 1; y < sourceHeight - 1; y++) 
  1012.         {
  1013.             // MIDDLE
  1014.             
  1015.             src[y * srcRowBytes + x];
  1016.         }
  1017.     }
  1018. */
  1019.          
  1020.          
  1021. #if 0    
  1022.     switch (type) 
  1023.     {
  1024.         case 1:    //Components planar in half row, order swapped (STV680-style)
  1025.             componentStep=1;
  1026.             green1Run =src+sourceWidth/2;
  1027.             red1Run   =src;
  1028.             blue1Run  =src+srcRowBytes+sourceWidth/2;
  1029.             green2Run =src+srcRowBytes;
  1030.             break;
  1031.         case 2:    //Interleaved data (STV600-style) // GRBG
  1032.         case 6: // works like 4 then switch R and B at the end // GBRG
  1033.             componentStep=2;
  1034.             green1Run =src;
  1035.             red1Run   =src+1;
  1036.             blue1Run  =src+srcRowBytes;
  1037.             green2Run =src+srcRowBytes+1;
  1038.             break;
  1039.         case 3:    //Row 1: xGxG, Row 2: RBRB (QuickCam Pro subsampled-style)
  1040.             componentStep=2;
  1041.             red1Run   =src+srcRowBytes+1;
  1042.             green1Run =src+1;
  1043.             green2Run =src+1;
  1044.             blue1Run  =src+srcRowBytes;
  1045.             break;
  1046.         case 8: // Cy Ye Gr Mg weirdness
  1047.             CyYeGrMgtype = YES;
  1048.         case 4:    // OV7630 style // BGGR
  1049.         case 5: // works like 4 then switch R and B at the end // RGGB
  1050.             GRBGtype = NO;
  1051.             componentStep=2;
  1052.             blue1Run  =src;
  1053.             green1Run =src+1;
  1054.             green2Run =src+srcRowBytes;
  1055.             red1Run   =src+srcRowBytes+1;
  1056.             break;
  1057.         default: //Assume type 2
  1058. #ifdef VERBOSE
  1059.             NSLog(@"BayerConverter: Unknown bayer data type: %i",type);
  1060. #endif VERBOSE
  1061.             componentStep=2;
  1062.             green1Run =src;
  1063.             red1Run   =src+1;
  1064.             blue1Run  =src+srcRowBytes;
  1065.             green2Run =src+srcRowBytes+1;
  1066.             break;
  1067.     }
  1068.     
  1069.     //init data run pointers
  1070.     srcSkip =2*srcRowBytes-(((sourceWidth-2)/2)*componentStep);
  1071.     // componentStep is added here to compensate for the initial subtraction in the big loop below
  1072.     // the loop over the non-border rows starts with the runs pointing to the left half
  1073.     // one could probably eliminate both adding it here and subtracting it later
  1074.     green3Run =green1Run+2*srcRowBytes+componentStep;
  1075.     red2Run   =red1Run+2*srcRowBytes+componentStep;
  1076.     blue2Run  =blue1Run+2*srcRowBytes+componentStep;
  1077.     green4Run =green2Run+2*srcRowBytes+componentStep;
  1078.     dst1Run=rgbBuffer;
  1079.     dst2Run=rgbBuffer+2*dstSkip;
  1080.     
  1081.     //First row, first column
  1082.     *(dst1Run++)=*red1Run;
  1083.     *(dst1Run++)=(GRBGtype)?*green1Run:(*green1Run+*green2Run)/2;
  1084.     *(dst1Run++)=*blue1Run;
  1085.     //First row, non-border columns
  1086.     for (x=(sourceWidth-2)/2;x>0;x--) {
  1087.         *(dst1Run++)=*red1Run;
  1088.         *(dst1Run++)=(GRBGtype)?(*green1Run+*(green1Run+componentStep)+*green2Run)/3:*green1Run;
  1089.         *(dst1Run++)=(*blue1Run+*(blue1Run+componentStep))/2;
  1090.         if (GRBGtype) green1Run+=componentStep;
  1091.         green2Run+=componentStep;
  1092.         blue1Run+=componentStep;
  1093.         *(dst1Run++)=(*red1Run+*(red1Run+componentStep))/2;
  1094.         *(dst1Run++)=(GRBGtype)?*green1Run:(*green1Run+*(green1Run+componentStep)+*green2Run)/3;
  1095.         *(dst1Run++)=*blue1Run;
  1096.         red1Run+=componentStep;
  1097.         if (!GRBGtype) green1Run+=componentStep;
  1098.     }
  1099.     //First row, last column
  1100.     *(dst1Run++)=*red1Run;
  1101.     *(dst1Run++)=(GRBGtype)?(*green1Run+*green2Run)/2:*green1Run;
  1102.     *(dst1Run++)=*blue1Run;
  1103.     //Reset the src data run pointers we changed in the first row - dst1RUn is ok now
  1104.     green1Run =green3Run-2*srcRowBytes;
  1105.     red1Run   =red2Run-2*srcRowBytes;
  1106.     blue1Run  =blue2Run-2*srcRowBytes;
  1107.     green2Run =green4Run-2*srcRowBytes;
  1108.     
  1109.     
  1110.     //All non-border rows
  1111.     for (y=(sourceHeight-2)/2;y>0;y--) {
  1112.         //init right half of colors to left values - will be shifted inside the loop
  1113.         r2=*(red1Run-componentStep);    
  1114.         r4=*(red2Run-componentStep);
  1115.         g2=*(green1Run-componentStep);
  1116.         g4=*(green2Run-componentStep);
  1117.         g6=*(green3Run-componentStep);
  1118.         g8=*(green4Run-componentStep);
  1119.         b2=*(blue1Run-componentStep);
  1120.         b4=*(blue2Run-componentStep);
  1121.         
  1122.         //First pixel column in row
  1123.         *(dst1Run++)=(GRBGtype)?(r2+r4)/2:r2;
  1124.         *(dst1Run++)=(GRBGtype)?(g2+g4+g6)/3:g2;
  1125.         *(dst1Run++)=(GRBGtype)?b2:(b2+b4)/2;
  1126.         *(dst2Run++)=(GRBGtype)?r4:(r2+r4)/2;
  1127.         *(dst2Run++)=(GRBGtype)?g6:(g4+g6+g8)/3;
  1128.         *(dst2Run++)=(GRBGtype)?(b2+b4)/2:b4;
  1129.         
  1130.         //All non-border columns in row
  1131.         for (x=(sourceWidth-2)/2;x>0;x--) {
  1132.             //shift right half of colors to left half
  1133.             r1=r2;
  1134.             r3=r4;
  1135.             g1=g2;
  1136.             g3=g4;
  1137.             g5=g6;
  1138.             g7=g8;
  1139.             b1=b2;
  1140.             b3=b4;
  1141.             //read new ones
  1142.             r2=*red1Run; red1Run+=componentStep;
  1143.             g2=*green1Run; green1Run+=componentStep;
  1144.             g4=*green2Run; green2Run+=componentStep;
  1145.             b2=*blue1Run; blue1Run+=componentStep;
  1146.             r4=*red2Run; red2Run+=componentStep;
  1147.             g6=*green3Run; green3Run+=componentStep;
  1148.             g8=*green4Run; green4Run+=componentStep;
  1149.             b4=*blue2Run; blue2Run+=componentStep;
  1150.             
  1151.             //Interpolate Pixel (2,2): location of g3 (r1).
  1152.             *(dst1Run++)=(GRBGtype)?(r1+r3)/2:r1;
  1153.             *(dst1Run++)=(GRBGtype)?g3:(g1+g3+g4+g5)/4;
  1154.             *(dst1Run++)=(GRBGtype)?(b1+b2)/2:(b1+b2+b3+b4)/4;
  1155.             //Interpolate Pixel (3,2): location of b2 (g4).
  1156.             *(dst1Run++)=(GRBGtype)?(r1+r2+r3+r4)/4:(r1+r2)/2;
  1157.             *(dst1Run++)=(GRBGtype)?(g2+g3+g4+g6)/4:g4;
  1158.             *(dst1Run++)=(GRBGtype)?b2:(b2+b4)/2;
  1159.             //Interpolate Pixel (2,3): location of r3 (g5).
  1160.             *(dst2Run++)=(GRBGtype)?r3:(r1+r3)/2;
  1161.             *(dst2Run++)=(GRBGtype)?(g3+g5+g6+g7)/4:g5;
  1162.             *(dst2Run++)=(GRBGtype)?(b1+b2+b3+b4)/4:(b3+b4)/2;
  1163.             //Interpolate Pixel (3,3): location of g6 (b4).
  1164.             *(dst2Run++)=(GRBGtype)?(r3+r4)/2:(r1+r2+r3+r4)/4;
  1165.             *(dst2Run++)=(GRBGtype)?g6:(g4+g5+g6+g8)/4;
  1166.             *(dst2Run++)=(GRBGtype)?(b2+b4)/2:b4;
  1167.         }
  1168.         
  1169.         //last pixel column in row
  1170.         *(dst1Run++)=(GRBGtype)?(r2+r4)/2:r2;
  1171.         *(dst1Run++)=(GRBGtype)?g4:(g2+g4+g6)/3;
  1172.         *(dst1Run++)=(GRBGtype)?b2:(b2+b4)/2;
  1173.         *(dst2Run++)=(GRBGtype)?r4:(r2+r4)/2;
  1174.         *(dst2Run++)=(GRBGtype)?(g4+g6+g8)/3:g6;
  1175.         *(dst2Run++)=(GRBGtype)?(b2+b4)/2:b4;
  1176.         
  1177.         //go to start of next two lines
  1178.         dst1Run+=dstSkip;
  1179.         dst2Run+=dstSkip;
  1180.         red1Run+=srcSkip;
  1181.         red2Run+=srcSkip;
  1182.         green1Run+=srcSkip;
  1183.         green2Run+=srcSkip;
  1184.         green3Run+=srcSkip;
  1185.         green4Run+=srcSkip;
  1186.         blue1Run+=srcSkip;
  1187.         blue2Run+=srcSkip;
  1188.     }
  1189.     // corrections
  1190.     red1Run   += srcRowBytes - srcSkip;
  1191.     red2Run   += srcRowBytes - srcSkip;
  1192.     green1Run += srcRowBytes - srcSkip;
  1193.     green2Run += srcRowBytes - srcSkip;
  1194.     green3Run += srcRowBytes - srcSkip;
  1195.     green4Run += srcRowBytes - srcSkip;
  1196.     blue1Run  += srcRowBytes - srcSkip;
  1197.     blue2Run  += srcRowBytes - srcSkip;
  1198.     //Last row, first column
  1199.     *(dst1Run++)=*red1Run;
  1200.     *(dst1Run++)=(GRBGtype)?(*green1Run+*green2Run)/2:*green1Run;
  1201.     *(dst1Run++)=*blue1Run;
  1202.     //Last row, non-border columns
  1203.     for (x=(sourceWidth-2)/2;x>0;x--) {
  1204.         *(dst1Run++)=*red1Run;
  1205.         *(dst1Run++)=(GRBGtype)?*green2Run:(*green1Run+*green2Run+*(green2Run+componentStep))/3;
  1206.         *(dst1Run++)=(*blue1Run+*(blue1Run+componentStep))/2;
  1207.         green1Run+=componentStep;
  1208.         blue1Run+=componentStep;
  1209.         *(dst1Run++)=(*red1Run+*(red1Run+componentStep))/2;
  1210.         *(dst1Run++)=(GRBGtype)?(*green1Run+*green2Run+*(green2Run+componentStep))/3:*(green2Run+componentStep);
  1211.         *(dst1Run++)=*blue1Run;
  1212.         red1Run+=componentStep;
  1213.         green2Run+=componentStep;
  1214.     }
  1215.     //Last row, last column
  1216.     *(dst1Run++)=*red1Run;
  1217.     *(dst1Run++)=(GRBGtype)?*green2Run:(*green1Run+*green2Run)/2;
  1218.     *(dst1Run++)=*blue1Run;
  1219.     
  1220.     if (type == 5 || type == 6) // RGGB or GBRG
  1221.     {
  1222.         for (y = 0; y < sourceHeight; y++) 
  1223.             for (x = 0; x < sourceWidth; x++) 
  1224.             {
  1225.                 unsigned char temp = rgbBuffer[3 * (x + y * sourceWidth) + 0]; // R
  1226.                 rgbBuffer[3 * (x + y * sourceWidth) + 0] = rgbBuffer[3 * (x + y * sourceWidth) + 2];
  1227.                 rgbBuffer[3 * (x + y * sourceWidth) + 2] = temp;
  1228.             }
  1229.     }
  1230. #endif
  1231. }
  1232.  
  1233.  
  1234. - (void) processTriplet: (UInt8 *) triplet
  1235. {
  1236.     int g =    triplet[1];
  1237.     int r = (((triplet[0] - g) * saturation) / 65536) + g;
  1238.     int b = (((triplet[2] - g) * saturation) / 65536) + g;
  1239.     
  1240.     triplet[0] = redTransferLookup[CLAMP(r,0,255)];
  1241.     triplet[1] = greenTransferLookup[CLAMP(g,0,255)];
  1242.     triplet[2] = blueTransferLookup[CLAMP(b,0,255)];
  1243. }
  1244.  
  1245.  
  1246. - (void) processImage: (UInt8 *) buffer numRows: (long) numRows rowBytes: (long) rowBytes bpp: (short) bpp
  1247. {
  1248.     UInt8 * ptr;
  1249.     long  w, h;
  1250.     
  1251.     if (needsTransferLookup) 
  1252.         for (h = 0; h < numRows; h++) 
  1253.         {
  1254.             ptr = buffer + h * rowBytes;
  1255.             
  1256.             if (bpp == 4) 
  1257.                 ptr++;
  1258.             
  1259.             for (w = 0; w < rowBytes; w += bpp, ptr += bpp) 
  1260.                 [self processTriplet:ptr];
  1261.         }
  1262. }
  1263.  
  1264.  
  1265. - (void) postprocessGRBGTo: (unsigned char*) dst dstRowBytes: (long) dstRB dstBPP: (short) dstBPP flip: (BOOL) flip
  1266. {
  1267.     // copy rgbBuffer to dst
  1268.     
  1269.     unsigned char * src = rgbBuffer;
  1270.     unsigned char * dst_saved = dst;
  1271.     
  1272.     int width  = MIN(sourceWidth, destinationWidth);
  1273.     int height = MIN(sourceHeight, destinationHeight);
  1274.     int srcBPP = 3;
  1275.     int srcRB = sourceWidth * srcBPP;
  1276.     int srcRowSkip = srcRB - width * srcBPP;
  1277.     int dstRowSkip = dstRB - width * dstBPP;
  1278.     int x,y;
  1279.     
  1280.     for (y = 0; y < height; y++) 
  1281.     {
  1282.         for (x = 0; x < height; x++) 
  1283.         {
  1284.             if (dstBPP == 4) *(dst++) = 255;
  1285.             *(dst++) = *(src++);
  1286.             *(dst++) = *(src++);
  1287.             *(dst++) = *(src++);
  1288.         }
  1289.         
  1290.         src += srcRowSkip;
  1291.         dst += dstRowSkip;
  1292.     }
  1293.     
  1294.     [self processImage:dst_saved numRows:height rowBytes:dstRB bpp:dstBPP];
  1295. }
  1296.  
  1297. @end
  1298.